#pragma once

#include <entt/entt.hpp>

#include <iostream>
#include <stdexcept>
#include <tuple>
#include <type_traits>

namespace qbe {

template <typename Registry, typename Tuple, std::size_t... Is>
void create_components_impl(Registry& registry, entt::entity entity, const Tuple&, std::index_sequence<Is...>) {}

template <typename Registry, typename... Args, std::size_t... Is>
void create_components_impl(Registry& registry, entt::entity entity, const std::tuple<Args...>& tuple, std::index_sequence<Is...>) {
  (registry.emplace<Args>(entity, std::get<Is>(tuple)), ...);
}

template <typename>
void remove_entity_component(entt::registry& registry, entt::entity entity) {}

template <typename T, typename... Args>
void remove_entity_component(entt::registry& registry, entt::entity entity) {
  registry.erase<T>(entity);
  remove_entity_component<Args...>(registry, entity);
}

template <typename... Args>
struct Bundle {
  using Type = std::tuple<Args...>;
  Type type;

  template <typename T>
  T& get() {
    return std::get<T>(type);
  }

  struct Tag {};

  entt::entity create(entt::registry& registry) {
    entt::entity entity = registry.create();
    bind(registry, entity);
    return entity;
  }

  void bind(entt::registry& registry, entt::entity entity) {
    create_components_impl(registry, entity, type, std::index_sequence_for<Args...>{});
    if (!registry.all_of<Tag>(entity))
      registry.emplace<Tag>(entity);
  }

  static void remove(entt::registry& registry, entt::entity entity) { remove_entity_component<Args...>(registry, entity); }
};

}  // namespace qbe